import { _decorator, Component, Node, v3, js, ParticleSystem, CurveRange, GradientRange, Color, Gradient, ColorKey, AlphaKey, Burst, Enum, RealCurve, ExtrapolationMode, RealKeyframeValue } from 'cc';
const { ccclass, property ,executeInEditMode} = _decorator;

enum ShapeType {
    Box,
    Circle,
    Cone,
    Sphere,
    Hemisphere
}

enum EmitFrom {
    Base,
    Edge,
    Shell,
    Volume
}
const ArcMode = Enum({
    /**
     * 随机位置发射。
     */
    Random: 0,

    /**
     * 沿某一方向循环发射，每次循环方向相同。
     */
    Loop: 1,

    /**
     * 循环发射，每次循环方向相反。
     */
    PingPong: 2,
});

const ExtrapolationModeMap = {
    [0]:ExtrapolationMode.PING_PONG,
    [1]:ExtrapolationMode.LOOP,
    [2]:ExtrapolationMode.CLAMP
}
@ccclass('U3dParticle2Cocos')
@executeInEditMode
export class U3dParticle2Cocos extends Component {
    start() {

    }

    update(deltaTime: number) {
        
    }

    setColor(color:Color, color_json) {
        color.r = color_json.r * 255
        color.g = color_json.g * 255
        color.b = color_json.b * 255
        color.a = color_json.a * 255
    }

    applyTransform(node, json) {
        let p = json.m_LocalPosition
        node.position = p

        node.rotation = json.m_LocalRotation

        node.scale = json.m_LocalScale
    }

    applyRealCurve(curve: RealCurve, json) {
        curve.addKeyFrame
        curve.preExtrapolation = ExtrapolationModeMap[json.m_PreInfinity]
        curve.postExtrapolation = ExtrapolationModeMap[json.m_PostInfinity]

        for (let i = 0; i < json.m_Curve.length; i++) {
            let c = json.m_Curve[i]
            curve.addKeyFrame(c.time, c.value)
            let frame = curve.getKeyframeValue(i)
            frame.leftTangent = c.inSlope
            frame.rightTangent = c.outSlope

            // todo 3次插值时的权重设置
        }
    }

    applyCurveRange(curve:CurveRange, json) {
        curve.mode = json.minMaxState
        switch(curve.mode) {
            case CurveRange.Mode.Constant:
                curve.constant = json.scalar
                break
            case CurveRange.Mode.Curve:
                curve.spline
                this.applyRealCurve(curve.spline, json.maxCurve)
                break
            case CurveRange.Mode.TwoCurves:
                this.applyRealCurve(curve.splineMin, json.minCurve)
                this.applyRealCurve(curve.splineMax, json.maxCurve)
                break
            case CurveRange.Mode.TwoConstants:
                curve.constantMin = json.minScalar
                curve.constantMax = json.scalar
                break
        }
    }

    applyGradient(gradient:Gradient, json) {
        gradient.mode = json.m_Mode
        let colorKeys = []
        let alphaKeys = []
        if (json.m_NumColorKeys > 0) {
            for (let i = 0; i < json.m_NumColorKeys; i++) {
                let key = new ColorKey()
                this.setColor(key.color, json["key" + i])
                colorKeys.push(key)
            }
        }
        if (json.m_NUmAlphaKeys > 0) {
            for (let i = 0; i < json.m_NUmAlphaKeys; i++) {
                let key = new AlphaKey()
                key.alpha = json["key" + i].a * 255
                alphaKeys.push(key)
            }
        }
        gradient.setKeys(colorKeys, alphaKeys)
    }

    applyGradientRange(gradientRange: GradientRange, json) {
        gradientRange.mode = json.minMaxState
        switch(gradientRange.mode) {
            case GradientRange.Mode.Color:
                this.setColor(gradientRange.color, json.maxColor)
                break
            case GradientRange.Mode.Gradient:
                this.applyGradient(gradientRange.gradient, json.maxGradient)
                break
            case GradientRange.Mode.TwoColors:
                this.setColor(gradientRange.colorMin, json.minColor)
                this.setColor(gradientRange.colorMax, json.maxColor)
                break
            case GradientRange.Mode.TwoGradients:
                this.applyGradient(gradientRange.gradientMin, json.minGradient)
                this.applyGradient(gradientRange.gradientMax, json.maxGradient)
                break
        }
    }

    applyShapeArc(m, m_json) {
        m.arcMode = m_json.arc.mode
        m.arc = m_json.arc.value
        if (m.arcMode == ArcMode.Loop || m.arcMode == ArcMode.PingPong) {
            m.arcSpread = m_json.arc.spreed
            this.applyCurveRange(m.arcSpeed, m_json.arc.speed)
        }
    }

    applyShapeModule(cmp:ParticleSystem, m_json) {
        let m = cmp.shapeModule
        switch(m_json.type) {
            case 5:
                m.shapeType = ShapeType.Box
                m.emitFrom = EmitFrom.Volume
                break
            case 15:
                m.shapeType = ShapeType.Box
                m.emitFrom = EmitFrom.Shell
                break
            case 16:
                m.shapeType = ShapeType.Box
                m.emitFrom = EmitFrom.Edge
                break
            case 10:
                m.shapeType = ShapeType.Circle
                break
            case 4:
                m.shapeType = ShapeType.Cone
                m.emitFrom = EmitFrom.Base
                break
            case 8:
                m.shapeType = ShapeType.Cone
                m.emitFrom = EmitFrom.Volume
                break
            case 0:
                m.shapeType = ShapeType.Sphere
                break
            case 2: 
                m.shapeType = ShapeType.Hemisphere
                break
        }
        m.position.set(m_json.m_Position)
        m.rotation.set(m_json.m_Rotation)
        m.scale.set(m_json.m_Scale)
        m.alignToDirection = !!m_json.alignToDirection
        m.randomDirectionAmount = m_json.randomDirectionAmount
        m.sphericalDirectionAmount = m_json.sphericalDirectionAmount
        m.randomPositionAmount = m_json.randomPositionAmount

        switch(m.shapeType) {
            case ShapeType.Box:
                m.boxThickness.set(m_json.boxThickness)
                break
            case ShapeType.Circle:
                m.radius = m_json.radius.value
                this.applyShapeArc(m, m_json)
                break
            case ShapeType.Cone:
                m.radius = m_json.radius.value
                m.radiusThickness = m_json.radiusThickness
                m.angle = m_json.angle
                this.applyShapeArc(m, m_json)
                m.length = m_json.length
                break
            case ShapeType.Sphere:
                m.radius = m_json.radius.value
                m.radiusThickness = m_json.radiusThickness
                this.applyShapeArc(m, m_json)
                break
            case ShapeType.Hemisphere:
                m.radius = m_json.radius.value
                m.radiusThickness = m_json.radiusThickness
                this.applyShapeArc(m, m_json)
                break
        }
    }

    applyVelocityOvertimeModule(cmp:ParticleSystem, m_json) {
        let m = cmp.velocityOvertimeModule
        this.applyCurveRange(m.x, m_json.x)
        this.applyCurveRange(m.y, m_json.y)
        this.applyCurveRange(m.z, m_json.z)
        this.applyCurveRange(m.speedModifier, m_json.speedModifier)

        if (m_json.inWorldSpace == 0) {
            m.space = 1
        } else if (m_json.inWorldSpace == 1) {
            m.space = 0
        }
    }

    applyForceOvertimeModule(cmp:ParticleSystem, m_json) {
        let m = cmp.forceOvertimeModule
        this.applyCurveRange(m.x, m_json.x)
        this.applyCurveRange(m.y, m_json.y)
        this.applyCurveRange(m.z, m_json.z)
        if (m_json.inWorldSpace == 0) {
            m.space = 1
        } else if (m_json.inWorldSpace == 1) {
            m.space = 0
        }
    }

    applySizeOvertimeModule(cmp: ParticleSystem, m_json) {
        let m = cmp.sizeOvertimeModule
        m.separateAxes = !!m_json.separateAxes
        this.applyCurveRange(m.size, m_json.curve)

        if (m.separateAxes) {
            this.applyCurveRange(m.x, m_json.curve)
            this.applyCurveRange(m.y, m_json.y)
            this.applyCurveRange(m.z, m_json.z)
        }
    }

    applyRotationOvertimeModule(cmp: ParticleSystem, m_json) {
        let m = cmp.rotationOvertimeModule
        m.separateAxes = !!m_json.separateAxes
        this.applyCurveRange(m.z, m_json.curve)
        if (m.separateAxes) {
            this.applyCurveRange(m.x, m_json.x)
            this.applyCurveRange(m.y, m_json.y)
        }
    }

    applyColorOverLifetimeModule(cmp: ParticleSystem, m_json) {
        let m = cmp.colorOverLifetimeModule
        this.applyGradientRange(m.color, m_json.gradient)
    }

    applyTextureAnimationModule(cmp: ParticleSystem, m_json) {
        if (m_json.mode != 0) {
            console.warn("cocos creator的TextureAnimationModule目前只支持gird模式")
        }
        let m = cmp.textureAnimationModule
        m.numTilesX = m_json.tilesX
        m.numTilesY = m_json.tilesY
        m.animation = m_json.animationType
        this.applyCurveRange(m.frameOverTime, m_json.frameOverTime)
        this.applyCurveRange(m.startFrame, m_json.startFrame)
        m.cycleCount = m_json.cycles

        m.randomRow = m_json.rowMode == 1
        if (m_json.rowMode == 0) {
            m.rowIndex = m_json.rowIndex
        }
        if (m_json.rowMode == 2) {
            console.warn("u3d粒子系统 textureAnimationModule中的rowMode模式meshIndex cocos creator中没有支持")
        }
    }

    applyLimitVelocityOvertimeModule(cmp: ParticleSystem, m_json) {
        let m = cmp.limitVelocityOvertimeModule
        if (m_json.inWorldSpace == 0) {
            m.space = 1
        } else if (m_json.inWorldSpace == 1) {
            m.space = 0
        }

        m.dampen = m_json.dampen
        m.separateAxes = !!m_json.separateAxes
        if (!m.separateAxes) {
            this.applyCurveRange(m.limit, m_json.x)
        } else {
            this.applyCurveRange(m.limitX, m_json.x)
            this.applyCurveRange(m.limitY, m_json.y)
            this.applyCurveRange(m.limitZ, m_json.z)
        }
    }

    applyTrailModule(cmp: ParticleSystem, m_json) {
        if (m_json.mode != 0) {
            console.warn("cocos creator 粒子系统的拖尾目前支持支particles模式")
        }
        let m = cmp.trailModule
        this.applyCurveRange(m.lifeTime, m_json.lifeTime)
        m.minParticleDistance = m_json.minVertexDistance
        if (m_json.inWorldSpace == 0) {
            m.space = 1
        } else if (m_json.inWorldSpace == 1) {
            m.space = 0
        }
        m.widthFromParticle = !!m_json.sizeAffectsWidth
        m.widthRatio = m_json.ratio
        m.colorFromParticle = !!m_json.inheritParticleColor

        this.applyGradientRange(m.colorOverTrail, m_json.colorOverTrail)
        this.applyGradientRange(m.colorOvertime, m_json.colorOverLifetime)
    }

    // 根据读取的u3d数据设置粒子系统参数
    applyParticleSystem(node, json) {
        let cmp:ParticleSystem = node.addComponent(ParticleSystem)

        cmp.duration = json.lengthInSec
        cmp.capacity = json.InitialModule.maxNumParticles
        cmp.loop = !!json.looping
        cmp.playOnAwake = !!json.playOnAwake
        
        // cmp.simulationSpace
        cmp.simulationSpeed = json.simulationSpeed

        this.applyCurveRange(cmp.startDelay, json.startDelay)
        this.applyCurveRange(cmp.startLifetime, json.InitialModule.startLifetime)
        this.applyGradientRange(cmp.startColor, json.InitialModule.startColor)

        cmp.scaleSpace = json.scalingMode

        cmp.startSize3D = !!json.InitialModule.size3D
        this.applyCurveRange(cmp.startSizeX, json.InitialModule.startSize)
        if (cmp.startSize3D) {
            this.applyCurveRange(cmp.startSizeY, json.InitialModule.startSizeY)
            this.applyCurveRange(cmp.startSizeZ, json.InitialModule.startSizeZ)
        }
        this.applyCurveRange(cmp.startSpeed, json.InitialModule.startSpeed)

        cmp.startRotation3D = !!json.InitialModule.rotation3D
        if (cmp.startRotation3D) {
            this.applyCurveRange(cmp.startRotationX, json.InitialModule.startRotationX)
            this.applyCurveRange(cmp.startRotationX, json.InitialModule.startRotationY)
            this.applyCurveRange(cmp.startRotationX, json.InitialModule.startRotation)
        }

        this.applyCurveRange(cmp.gravityModifier, json.InitialModule.gravityModifier)
        this.applyCurveRange(cmp.rateOverTime, json.EmissionModule.rateOverTime)
        this.applyCurveRange(cmp.rateOverDistance, json.EmissionModule.rateOverDistance)

        if (json.EmissionModule.m_BurstCount > 0) {
            let bursts = []
            for (let i = 0; i < json.EmissionModule.m_BurstCount; i++) {
                let json_burst = json.EmissionModule.m_Bursts[i]
                let burst = new Burst()
                burst.repeatCount = json_burst.cycleCount == 0 ? Infinity : json_burst.cycleCount
                this.applyCurveRange(burst.count, json_burst.countCurve)
                burst.repeatInterval = json_burst.repeatInterval
                burst.time = json_burst.time
                bursts.push(burst)
            }
            cmp.bursts = bursts
        }

        // 噪声图模块
        cmp.noiseModule.enable = !!json.NoiseModule.enabled
        if (cmp.noiseModule.enable) {
            cmp.noiseModule.octaves = json.NoiseModule.octaves
            if (cmp.noiseModule.octaves > 1) {
                cmp.noiseModule.octaveScale = json.NoiseModule.octaveScale
                cmp.noiseModule.octaveMultiplier = json.NoiseModule.octaveMultiplier
            }
            cmp.noiseModule.noiseFrequency = json.NoiseModule.frequency

            if (json.NoiseModule.strength.minMaxState != 0) {
                console.warn("cocos creator粒子系统的noiseModule的strength是一个固定值！")
            }
            if (json.NoiseModule.strengthY.minMaxState != 0) {
                console.warn("cocos creator粒子系统的noiseModule的strength是一个固定值！")
            }
            if (json.NoiseModule.strengthZ.minMaxState != 0) {
                console.warn("cocos creator粒子系统的noiseModule的strength是一个固定值！")
            }
            cmp.noiseModule.strengthX = json.NoiseModule.strength.scalar
            cmp.noiseModule.strengthY = json.NoiseModule.strengthY.scalar
            cmp.noiseModule.strengthZ = json.NoiseModule.strengthZ.scalar
        }

        // 形状模块
        cmp.shapeModule.enable = !!json.ShapeModule.enabled
        if (cmp.shapeModule.enable) {
            this.applyShapeModule(cmp, json.ShapeModule)
        }

        // console.log("velocityOvertimeModule")
        cmp.velocityOvertimeModule.enable = !!json.VelocityModule.enabled
        if (cmp.velocityOvertimeModule.enable) {
            this.applyVelocityOvertimeModule(cmp, json.VelocityModule)
        }
        // console.log("forceOvertimeModule")
        cmp.forceOvertimeModule.enable = !!json.ForceModule.enabled
        if (cmp.forceOvertimeModule.enable) {
            this.applyForceOvertimeModule(cmp, json.ForceModule)
        }
        // console.log("sizeOvertimeModule")
        cmp.sizeOvertimeModule.enable = !!json.SizeModule.enabled
        if (cmp.sizeOvertimeModule.enable) {
            this.applySizeOvertimeModule(cmp, json.SizeModule)
        }
        // console.log("rotationOvertimeModule")
        cmp.rotationOvertimeModule.enable = !!json.RotationModule.enabled
        if (cmp.rotationOvertimeModule.enable) {
            this.applyRotationOvertimeModule(cmp, json.RotationModule)
        }
        // console.log("colorOverLifetimeModule")
        cmp.colorOverLifetimeModule.enable = !!json.ColorModule.enabled
        if (cmp.colorOverLifetimeModule.enable) {
            this.applyColorOverLifetimeModule(cmp, json.ColorModule)
        }
        // console.log("textureAnimationModule")
        cmp.textureAnimationModule.enable = !!json.UVModule.enabled
        if (cmp.textureAnimationModule.enable) {
            this.applyTextureAnimationModule(cmp, json.UVModule)
        }
        // console.log("limitVelocityOvertimeModule")
        cmp.limitVelocityOvertimeModule.enable = !!json.ClampVelocityModule.enabled
        if (cmp.limitVelocityOvertimeModule.enable) {
            this.applyLimitVelocityOvertimeModule(cmp, json.ClampVelocityModule)
        }
        // console.log("trailModule")
        cmp.trailModule.enable = !!json.TrailModule.enabled
        if (cmp.trailModule.enable) {
            this.applyTrailModule(cmp, json.TrailModule)
        }
    }

    applyParticleSystemRenderer(node, json) {
        let cmp:ParticleSystem = node.getComponent(ParticleSystem)

        let m = cmp.renderer
        m.renderMode = json.m_RenderMode
        if (json.m_RenderMode == 5) {
            console.warn("cocos creator 暂不支持none模式的renderMode")
        }
    }

    createNode(parent:Node, n, nodesMap) {
        console.log("创建节点", n.json.m_Name)
        let node = new Node()
        node.name = n.json.m_Name
        parent.addChild(node)
        for(let i = 0; i < n.cmps.length; i++) {
            let cmpJson = n.cmps[i]
            if (cmpJson.u3dType == "Transform") {
                this.applyTransform(node, cmpJson)
            } else if (cmpJson.u3dType == "ParticleSystem") {
                this.applyParticleSystem(node, cmpJson)
            } else if (cmpJson.u3dType == "ParticleSystemRenderer") {
                this.applyParticleSystemRenderer(node, cmpJson)
            }
        }
        if (n.children.length > 0) {
            for (let i = 0; i < n.children.length; i++) {
                let fileID = n.children[i].fileID
                let child = nodesMap[fileID]
                if (!child) {
                    console.log("fileID:" + fileID + " child is not exist")
                }
                this.createNode(node, child, nodesMap)
            }
        }
    }

    createPrefab(jsons) {
        let nodes = []
        let cmps = []
        let nodesMap = {}
        let cmpsMap = []
        // let transforms = {}
        jsons.forEach(json => {
            if (json.u3dType == "GameObject") {
                nodes.push({
                    json:json,
                    cmps:[]
                })
            } else {
                cmpsMap[json.fileID] = json
                cmps.push(json)
            }
        });

        let root
        nodes.forEach(n=>{
            for (let i = 0; i < n.json.m_Component.length; i++) {
                let cmpID = n.json.m_Component[i].component.fileID
                let cmpJson = cmpsMap[cmpID]
                n.cmps.push(cmpJson)
                if (cmpJson.u3dType == "Transform") {
                    nodesMap[cmpJson.fileID] = n
                    n.children = cmpJson.m_Children
                    if (cmpJson.m_Father.fileID == "id0") {
                        root = n
                    }
                }
            }
        })
        if (!root) {
            console.log(cmps)
            console.log("no root")
        } else {
            console.log("创建预制体", root.json.m_Name)
            this.createNode(this.node, root, nodesMap)
        }
    }

    doConvert(files) {
        for (let i = 0; i < files.length; i++) {
            this.createPrefab(files[i])
        }
    }
}